
#include "stdafx.h"
#include "LineAnalyzer.h"
#include "StringExtension.h"



#define COMMENT_CHAR ';'						/* Comment character */
#define END_OF_LABEL ':'						/* End of label character */
#define START_OF_OPERATION '.'					/* Start character for operation */
#define PARAMETER_DELIMITER ','					/* Parameter delimiter for data and it is delimiter between operands as well */
#define STRING_CHAR '\"'						/* Strin character that bound a string in */
#define COMMAND_CHAR '/'						/* Command character for indicating number of bits and the type per operand*/
#define DIRECT_ADDRESS_CHAR '#'					/* Direct address character */
#define INDEX_ADDRESS_START_CHAR '{'			/* Start character of index address */
#define INDEX_ADDRESS_END_CHAR '}'				/* End character of index address */
#define INDEX_ADDRESS_LABEL_CHAR '*'			/* '*' indicates start of label in index address */


#define MAX_COMMAND_LENGTH 5					/* Max command length */
#define MAX_LABEL_LENGTH 30						/* Maximum label length */
#define MAX_NUMBER_AS_STRING_LENGTH 20			/* Number cannot contain more then 20 digits */

#define NUMBER_OF_OPERATIONS 4					/* Number of operations */
#define MAX_OPERATION_LENGTH 7					/* Maximum operation string length */

#define NUMBER_OF_REGISTERS 8					/* Registers r0 - r7 */
#define MAX_REGISTER_LENGTH 3					/* Maximum length of string is 3: rx + NULL */ 

#define COMMAND_BITS_OFFSET 1					/* Number of bots offset from the first slash */

#define COMMAND_10_BITS_LENGTH 5				/* Total length of the 10 Bits format is 5 characters */
#define COMMAND_10_BITS_SECOND_SLASH_OFFSET 2	/* 10 Bits - second slash offset from the first slash */
#define COMMAND_10_BITS_THIRD_SLASH_OFFSET 4	/* 10 Bits - third slash offset from the first slash  */
#define COMMAND_10_BITS_FIRST_OPERAND_OFFSET 3	/* 10 Bits - first operand offset from the first slash */
#define COMMAND_10_BITS_SECOND_OPERAND_OFFSET 5 /* 10 Bits - second operand offset from the first slash */
#define COMMAND_10_BITS_DBL_OFFSET 6			/* 10 Bits - dbl offset from the first slash */


#define COMMAND_20_BITS_LENGTH 1				/* Total length of the 20 Bits format is 1 characters */
#define COMMAND_20_BITS_DBL_OFFSET 2			/* 20 Bits - dbl offset from the first slash */

#define COMMAND_10_BITS 1						/* Command 10 bits is 1 */
#define COMMAND_20_BITS 0						/* Command 20 bits is 0 */

#define LEFT_BITS 0								/* Left bits is 0 */
#define RIGHT_BITS 1							/* Right bits is 0 */


/* Array that contains all the operations */
const char OPERATIONS_ARRAY[NUMBER_OF_OPERATIONS][MAX_OPERATION_LENGTH] = 
{ "data","string","entry","extern"};

/* Array that contains all the commands */
const char COMMANDS_ARRAY[NUMBER_OF_COMMANDS][MAX_COMMAND_LENGTH] = 
{ "mov","cmp","add","sub","not", "clr","lea", "inc", "dec", "jmp", "bne",
  "red", "prn", "jsr", "rts", "stop"};



/* Array that contains all legal immidiate addresses per comand. 
	First row is for the source operand, Second row is for the target operand.
	0 - means that the type of address is not legal.
	1 - means that the type of address is legal. */
const int LEGAL_ADDRESSES_ARRAY_0[2][NUMBER_OF_COMMANDS] = 
{ {1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, 
  {0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0}};

/* Array that contains all legal direct addresses per comand. 
	First row is for the source operand, Second row is for the target operand.
	0 - means that the type of address is not legal.
	1 - means that the type of address is legal. */
const int LEGAL_ADDRESSES_ARRAY_1[2][NUMBER_OF_COMMANDS] = 
{ {1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0}, 
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0}};

/* Array that contains all legal index addresses per comand. 
	First row is for the source operand, Second row is for the target operand.
	0 - means that the type of address is not legal.
	1 - means that the type of address is legal. */
const int LEGAL_ADDRESSES_ARRAY_2[2][NUMBER_OF_COMMANDS] = 
{ {1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0}, 
  {1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0}};

/* Array that contains all legal direct register addresses per comand. 
	First row is for the source operand, Second row is for the target operand.
	0 - means that the type of address is not legal.
	1 - means that the type of address is legal. */
const int LEGAL_ADDRESSES_ARRAY_3[2][NUMBER_OF_COMMANDS] = 
{ {1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0}, 
  {1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0}};

/* Array that contains all the register */ 
const char REGISTERS_ARRAY[NUMBER_OF_REGISTERS][MAX_REGISTER_LENGTH] = 
{ "r0","r1","r2","r3","r4", "r5", "r6", "r7" };


/*********************************************************************************************************************** */
/* Description: Check that this is a valid label */
/* Input:		 label - label to check */
/*********************************************************************************************************************** */
ERROR_TYPE CheckValidLabel(char* label)
{
	int len = strlen(label);
	/* Not empty... */
	if (len == 0)
		return ERROR_ILLEGAL_LABEL_SYNTAX;
	/* Not bigger then 30 chars */
	if (len > MAX_LABEL_LENGTH)
		return ERROR_ILLEGAL_LABEL_LENGTH;

	/* Check that the first letter is alphabetic character */
	if (!((label[0] >= 'a' && label[0] <= 'z') || (label[0] >= 'A' && label[0] <= 'Z')))
		return ERROR_ILLEGAL_LABEL_SYNTAX;

	/* Label is not a command */
	if (!CheckAlphanumericString(label))
		return ERROR_ILLEGAL_LABEL_SYNTAX;
	
		/* Label is not a command */
	if (GetPosOfStringInArray((char*)COMMANDS_ARRAY, NUMBER_OF_COMMANDS, MAX_COMMAND_LENGTH, label) != NOT_FOUND)
		return ERROR_ILLEGAL_LABEL_COMMAND_NAME;
	
	/* Label is not a register */
	if (GetPosOfStringInArray((char*)REGISTERS_ARRAY, NUMBER_OF_REGISTERS, MAX_REGISTER_LENGTH, label) != NOT_FOUND)
		return ERROR_ILLEGAL_LABEL_REGISTER_NAME;

	return ERROR_NONE;
}

/*********************************************************************************************************************** */
/* Description: Analyze data parameters and fill the operation structure accordingly */
/* Input:			parametersStart - Start pointer to the data parameters */
/* Input/Output:	pOperation - operation structure to fill */
/*********************************************************************************************************************** */
ERROR_TYPE FillOperationDataParameters(char* parametersStart, Operation* pOperation)
{
	/* Clear all tabs and spaces */
	char* parameters = ClearSpacesAndTabs(parametersStart);
	int len = strlen(parameters);
	/* No empty */
	if (len == 0)
		return ERROR_ILLEGAL_DATA_AT_LEAST_ONE_PARAMETER;
	int i;
	char numberAsString[MAX_NUMBER_AS_STRING_LENGTH];
	int parameterStartPos = 0;
	int parameterIndex = 0;
	int number;

	/* Go over eac charcter and search for the delimiter */
	for (i=0; i<len+1; i++)
	{
		/* If we found ',' or we have reached to the end */
		if (parameters[i] == PARAMETER_DELIMITER || parameters[i] == '\0')
		{
			/* Transfor the strin to integer and check that it is valid */
			memcpy(numberAsString, parameters + parameterStartPos, i-parameterStartPos);
			numberAsString[i-parameterStartPos] = '\0';
			if (StringToInteger(numberAsString, &number) == FALSE)
				return ERROR_ILLEGAL_DATA_PARAMETER_TYPE;

			/* Set the number in the array */
			(*pOperation).uData.intData[parameterIndex] = number;

			/* Set offset and increase counter */
			parameterStartPos = i+1;
			parameterIndex++;
		}
	}
	/* Set the data length */
	(*pOperation).dataLength = parameterIndex;

	return ERROR_NONE;
}

/*********************************************************************************************************************** */
/* Description: Analyze string and fill the operation structure accordingly */
/* Input:			parametersStart - Start pointer to the string */
/* Input/Output:	pOperation - operation structure to fill */
/*********************************************************************************************************************** */
ERROR_TYPE FillOperationString(char* parametersStart, Operation* pOperation)
{
	/* Trim the string */
	trim(parametersStart);

	if (parametersStart == NULL)
		return ERROR_ILLEGAL_STRING_PARAMETER;

	int len = strlen(parametersStart);
	
	/* Check that the string starts and ends with '"' character */
	if (parametersStart[0] != STRING_CHAR || parametersStart[len-1] != STRING_CHAR)
		return ERROR_ILLEGAL_STRING_PARAMETER;
	
	char* endString = strchr(parametersStart+1, STRING_CHAR);

	if (endString != (parametersStart+len-1))
		return ERROR_ILLEGAL_STRING_PARAMETER;

	/* Copy the string to the opeartion structure */
	memcpy((*pOperation).uData.stringData, parametersStart+1, endString - parametersStart - 1);
	(*pOperation).uData.stringData[endString - parametersStart - 1] = '\0';
	/* Set the data length */
	(*pOperation).dataLength = strlen((*pOperation).uData.stringData)+1;
	
	return ERROR_NONE;
}

/*********************************************************************************************************************** */
/* Description: Analyze string that should contain extern or entry and fill operation structure */
/* Input:			parametersStart - Start pointer to the label of extern/entry */
/* Input/Output:	pOperation - operation structure to fill */
/* Input:			errorLabelLength - Type of error for label length */
/* Input:			errorLabelSyntax - Type of error for label syntax  */
/*********************************************************************************************************************** */
ERROR_TYPE FillOperationLabel(char* parametersStart, Operation* pOperation, ERROR_TYPE errorLabelLength, ERROR_TYPE errorLabelSyntax)
{
	/* Trim the text */
	trim(parametersStart);

	if (parametersStart == NULL)
		return errorLabelSyntax;

	/* Check that the label is valid */
	ERROR_TYPE error = CheckValidLabel(parametersStart);
	
	if (error == ERROR_NONE)
	{
		/* Copy the label */
		int len = strlen(parametersStart);
		memcpy((*pOperation).uData.stringData, parametersStart, len);
		(*pOperation).uData.stringData[len] = '\0';
		return ERROR_NONE;
	}

	if (error == ERROR_ILLEGAL_LABEL_LENGTH)
		return errorLabelLength;

	return errorLabelSyntax;
}

/*********************************************************************************************************************** */
/* Description: Analyze string and fill operation structure */
/* Input:			operationStart - Start pointer to the operation */
/* Input/Output:	pOperation - operation structure to fill */
/*********************************************************************************************************************** */
ERROR_TYPE FillOperationDetails(char* operationStart, Operation* pOperation)
{
	int i;
	char* space = SearchSpace(operationStart);

	if (space == NULL)
		return ERROR_ILLEGAL_OPERATION;

	int len = space - operationStart - 1;

	/* Find the relevant operation */
	for (i=0; i< NUMBER_OF_OPERATIONS; i++)
	{
		if (strncmp(operationStart+1, OPERATIONS_ARRAY[i], strlen(OPERATIONS_ARRAY[i])) == 0)
		{
			(*pOperation).operationType = (OPERATION_TYPE)(i+1);
			break;
		}
	}

	/* Check that the operation was found */
	if (i >= NUMBER_OF_OPERATIONS)
		return ERROR_ILLEGAL_OPERATION;

	/* According to the operation fill the operation structure accordingly */
	switch ((*pOperation).operationType)
	{
	case OPERATION_DATA:
		return FillOperationDataParameters(operationStart + len + 1, pOperation);
	case OPERATION_STRING:
		return FillOperationString(operationStart + len + 1, pOperation);
	case OPERATION_ENTRY:
		return FillOperationLabel(operationStart + len + 1, pOperation, ERROR_ILLEGAL_ENTRY_LABEL_LENGTH, ERROR_ILLEGAL_ENTRY_LABEL_NAME);
	case OPERATION_EXTERN:
		return FillOperationLabel(operationStart + len + 1, pOperation, ERROR_ILLEGAL_EXTERN_LABEL_LENGTH, ERROR_ILLEGAL_EXTERN_LABEL_NAME);
	default:
		return ERROR_ILLEGAL_OPERATION;
	}
}

/*********************************************************************************************************************** */
/* Description: Return an error according to the expected operands */
/* Input:			expectedOperands - Expected operands */
/*********************************************************************************************************************** */
ERROR_TYPE GetErrorTypeAccordingToExpectedOperands(int expectedOperands)
{
	switch (expectedOperands)
	{
	case 0:
		return ERROR_ILLEGAL_NUMBER_OF_OPERANDS_EXPECTED_ZERO_OPERANDS;
	case 1:
		return ERROR_ILLEGAL_NUMBER_OF_OPERANDS_EXPECTED_ONE_OPERAND;
	case 2:
		return ERROR_ILLEGAL_NUMBER_OF_OPERANDS_EXPECTED_TWO_OPERANDS;
	}

	return ERROR_NONE;
}

/*********************************************************************************************************************** */
/* Description: Fill single operand details (source or destination) */
/* Input:			operandStart - Start of the operand string */
/* Input:			len - Length of operand string */
/* Input/Output:	pCommand - Command structure to fill with the details of operand */
/* Input:			operandIndex - Operand index */
/* Input:			expectedOperands - Number of expected operands according to the type of command */
/*********************************************************************************************************************** */
ERROR_TYPE FillOperand(char* operandStart, int len, Command* pCommand, int operandIndex, int expectedOperands)
{
	char operand[MAX_LINE_LENGTH];			/* String to contain the operand */
	char numberAsString[MAX_LINE_LENGTH];	/* Number as string when is needed */
	char registerStr[MAX_LINE_LENGTH];		/* String for register */
	int number;								/* auxilary number */
	int legalOperandsPos = 0;				/* Current Operand position */

	/* If there is only one operand then we should treat it as destination operand, therefore the position is 1 */
	if (expectedOperands == 1)
		legalOperandsPos = 1;

	/* If there is two operands then we should treat the operandIndex */
	if (expectedOperands == 2)
		legalOperandsPos = operandIndex;

	/* Trim the string */
	len = trim(operandStart, len);

	/* Copy the operand */
	memcpy(operand, operandStart, len);
	operand[len]='\0';

	/* Handle immidiate address (0) */
	if (operand[0] == DIRECT_ADDRESS_CHAR)
	{
		/* Verify legal address type for the command and for the current operand */
		if (LEGAL_ADDRESSES_ARRAY_0[legalOperandsPos][pCommand->commandType] == 0)
			return ERROR_ILLEGAL_ADDRESS_TYPE_FOR_THE_COMMAND;

		/* Copy the number and validate it */
		memcpy(numberAsString, operand + 1, len - 1);
		numberAsString[len - 1] = '\0';
		if (StringToInteger(numberAsString, &number) == FALSE)
			return ERROR_ILLEGAL_OPERAND_TYPE;

		/* Set operand to be immidiate only if it is on the first operand else - error */
		if (operandIndex == 0)
			pCommand->addresses[legalOperandsPos] = ADDRESS_IMMIDIATE;
		else
			return ERROR_ILLEGAL_DIRECT_ADDRESS_TO_DESTINATION;

		/* Copy the number to the additional word */
		memcpy(pCommand->additionalWords[pCommand->additionalWordsCount], operandStart + 1, len - 1);
		pCommand->additionalWords[pCommand->additionalWordsCount][len - 1] = '\0';
		pCommand->additionalWordsCount++;

		return ERROR_NONE;
	}

	/* Index address (2) */
	char* indexStart = strchr(operand, INDEX_ADDRESS_START_CHAR);
	if (indexStart != NULL)
	{
		/* Verify legal address type for the command and for the current operand */
		if (LEGAL_ADDRESSES_ARRAY_2[legalOperandsPos][pCommand->commandType] == 0)
			return ERROR_ILLEGAL_ADDRESS_TYPE_FOR_THE_COMMAND;

		char* indexEnd = strchr(operand, INDEX_ADDRESS_END_CHAR);

		if (indexEnd == NULL)
			return ERROR_ILLEGAL_ADDRESS_INDEX_FORMAT;
		
		pCommand->addresses[legalOperandsPos] = ADDRESS_INDEX;

		/* Copy the label to the additional words */
		memcpy(pCommand->additionalWords[pCommand->additionalWordsCount], operand, indexStart - operand);
		pCommand->additionalWords[pCommand->additionalWordsCount][indexStart - operand] = '\0';
		ERROR_TYPE error = CheckValidLabel(pCommand->additionalWords[pCommand->additionalWordsCount]);
		if (error != ERROR_NONE)
			return error;

		pCommand->additionalWordsCount++;

		/* Index Address - Label offset */
		char* labelIndexStart = strchr(operand, INDEX_ADDRESS_LABEL_CHAR);
		if (labelIndexStart != NULL)
		{
			/* Copy the label to the additional words */
			int labelLength = indexEnd - labelIndexStart - 1;
			memcpy(pCommand->additionalWords[pCommand->additionalWordsCount], labelIndexStart + 1, labelLength);
			pCommand->additionalWords[pCommand->additionalWordsCount][labelLength] = '\0';
			error = CheckValidLabel(pCommand->additionalWords[pCommand->additionalWordsCount]);
			if (error != ERROR_NONE)
				return error;

			pCommand->additionalWordsCount++;

			pCommand->addressIndexes[legalOperandsPos] = INDEX_LABEL;

			return ERROR_NONE;
		}

		/* Index Address - Number offset */
		len = indexEnd - indexStart - 1;
		memcpy(numberAsString, indexStart + 1, len);
		numberAsString[len] = '\0';
		if (StringToInteger(numberAsString, &number) == TRUE)
		{
			/* Copy the number to the additional words */
			memcpy(pCommand->additionalWords[pCommand->additionalWordsCount], numberAsString, len + 1);
			pCommand->additionalWordsCount++;

			pCommand->addressIndexes[legalOperandsPos] = INDEX_NUMBER;

			return ERROR_NONE;
		}

		/* Index Address - Register offset */
		memcpy(registerStr, indexStart + 1, len);
		registerStr[len] = '\0';
		int pos;
		if ((pos = GetPosOfStringInArray((char*)REGISTERS_ARRAY, NUMBER_OF_REGISTERS, MAX_REGISTER_LENGTH, registerStr)) != NOT_FOUND)
		{
			/* Set register */
			pCommand->registers[legalOperandsPos] = pos;
			pCommand->addressIndexes[legalOperandsPos] = INDEX_REGISTER;
			return ERROR_NONE;
		}

		return ERROR_ILLEGAL_ADDRESS_INDEX_FORMAT;
	}

	/* Direct register address (3) */
	int pos = GetPosOfStringInArray((char*)REGISTERS_ARRAY, NUMBER_OF_REGISTERS, MAX_REGISTER_LENGTH, operand);
	if (pos != NOT_FOUND)
	{
		if (LEGAL_ADDRESSES_ARRAY_3[legalOperandsPos][pCommand->commandType] == 0)
			return ERROR_ILLEGAL_ADDRESS_TYPE_FOR_THE_COMMAND;

		/* Set register */
		pCommand->addresses[legalOperandsPos] = ADDRESS_REGISTER_DIRECT;
		pCommand->registers[legalOperandsPos] = pos;

		return ERROR_NONE;

	}

	/* Direct address (1) */
	ERROR_TYPE error = CheckValidLabel(operand);
	if (error == ERROR_NONE)
	{
		if (LEGAL_ADDRESSES_ARRAY_1[legalOperandsPos][pCommand->commandType] == 0)
			return ERROR_ILLEGAL_ADDRESS_TYPE_FOR_THE_COMMAND;

		/* Copy the label to the additional words */
		memcpy(pCommand->additionalWords[pCommand->additionalWordsCount], operand, len);
		pCommand->additionalWords[pCommand->additionalWordsCount][len] = '\0';

		pCommand->additionalWordsCount++;
	
		pCommand->addresses[legalOperandsPos] = ADDRESS_DIRECT;

		return ERROR_NONE;
	}



	return ERROR_ILLEGAL_ADDRESS_TYPE;
}

/*********************************************************************************************************************** */
/* Description: Fill all the operands details */
/* Input:			commandStart - Start of the command string */
/* Input/Output:	pCommand - Command structure to fill with the details of the operands */
/* Input:			expectedOperands - Number of expected operands according to the type of command */
/*********************************************************************************************************************** */
ERROR_TYPE FillOperands(char* commandStart, Command* pCommand, int expectedOperands)
{
	int i;
	int len;
	int parameterIndex = 0;
	int parameterStartPos = 0;
	ERROR_TYPE error = ERROR_NONE;

	pCommand->additionalWordsCount = 0;

	/* Search some kind of space that indicates the start of the first operand */
	char* operandsStr = SearchSpace(commandStart);

	/* There is atleast one operand */
	if (operandsStr != NULL)
	{
		/* Trim the operands */
		trim(operandsStr);

		len = strlen(operandsStr);

		/* Go over all the characters and search for the end of the string or ',' character */
		for (i=0; i<len+1; i++)
		{
			if (operandsStr[i] == PARAMETER_DELIMITER || operandsStr[i] == '\0')
			{
				/* Raise an error if the current operand number is more then the number of expected operands */
				if (expectedOperands < parameterIndex + 1)
				{
					error  = GetErrorTypeAccordingToExpectedOperands(expectedOperands);
					if (error != ERROR_NONE)
						return error;
				}

				/* Fill current operand details */
				if ((error = FillOperand(operandsStr + parameterStartPos, i-parameterStartPos, pCommand, parameterIndex, expectedOperands)) != ERROR_NONE)
					return error;

				/* Increase current index if we haven't reach to the end */
				if (operandsStr[i] != '\0')
				{
					parameterStartPos = i+1;
					parameterIndex++;
				}
			}
		}
	}

	/* Raise an error if the expected operands is greater then the actual operands */
	if (expectedOperands > parameterIndex + 1)
	{
		error  = GetErrorTypeAccordingToExpectedOperands(expectedOperands);
		if (error != ERROR_NONE)
			return error;
	}

	return ERROR_NONE;

}

/*********************************************************************************************************************** */
/* Description: Fill dbl field in the command																			 */
/* Input:			dblStart - string to analyze dbl															*/
/* Input/Output:	pCommand - Command structure to fill with the dbl								*/
/* Output:			Error type					*/
/*********************************************************************************************************************** */
ERROR_TYPE FillDbl(char* dblStart, Command* pCommand)
{
	if (*dblStart != ',')
		ERROR_ILLEGAL_DBL_IN_COMMAND;

	if (*(dblStart+1) == '0')
		pCommand->dbl = 0;
	else if (*(dblStart+1) == '1')
		pCommand->dbl = 1;
	else
		return ERROR_ILLEGAL_DBL_VALUE;

	return ERROR_NONE;
}

/*********************************************************************************************************************** */
/* Description: Fill command details by analyzing the command string				 */
/* Input:			commandStart - string to analyze command															*/
/* Input/Output:	pCommand - Command structure to fill with the command details								*/
/* Output:			Error type					*/
/*********************************************************************************************************************** */
ERROR_TYPE FillCommandDetails(char* commandStart, Command* pCommand)
{
	int i;
	char* operandsStart = NULL;							/* Begin of each operand */
	char* slash = strchr(commandStart, COMMAND_CHAR);	/* First slash */
	char* comma = strchr(commandStart, PARAMETER_DELIMITER);	/* Operands delimiter */
	int firstOperandType = 0;							/* Type of the first operand, by it and the second operand we can determine the type of the operand */

	/* Illegal command */
	if (slash == NULL || comma == NULL)
		return ERROR_ILLEGAL_COMMAND;

	int len = slash - commandStart;

	/* Find the command type */
	for (i=0; i< NUMBER_OF_COMMANDS; i++)
	{
		if (strncmp(commandStart, COMMANDS_ARRAY[i], strlen(COMMANDS_ARRAY[i])) == 0)
		{
			(*pCommand).commandType = (COMMAND_TYPE)(i);
			break;
		}
	}

	if (i >= NUMBER_OF_COMMANDS)
		return ERROR_ILLEGAL_COMMAND;
	
	ERROR_TYPE error;

	/* Set the operand number bits type */
	if (comma - slash - 1 == COMMAND_20_BITS_LENGTH)
	{
		if (*(slash + COMMAND_BITS_OFFSET) == '0')
			(*pCommand).operandNumberBitsType = COMMAND_20_BITS;
		else
			return ERROR_ILLEGAL_COMMAND;
		
		error = FillDbl(slash + COMMAND_20_BITS_DBL_OFFSET, pCommand);
	}
	/* Set combination type */
	else if (comma - slash - 1 == COMMAND_10_BITS_LENGTH)
	{
		if (*(slash + COMMAND_10_BITS_SECOND_SLASH_OFFSET) != COMMAND_CHAR || *(slash + COMMAND_10_BITS_THIRD_SLASH_OFFSET) != COMMAND_CHAR)
			return ERROR_ILLEGAL_COMMAND;

		if (*(slash + COMMAND_BITS_OFFSET) == '1')
			(*pCommand).operandNumberBitsType = COMMAND_10_BITS;
		else
			return ERROR_ILLEGAL_COMMAND;

		if (*(slash + COMMAND_10_BITS_FIRST_OPERAND_OFFSET) == '0')
			firstOperandType = LEFT_BITS;
		else
			if (*(slash + COMMAND_10_BITS_FIRST_OPERAND_OFFSET) == '1')
				firstOperandType = RIGHT_BITS;
			else
				return ERROR_ILLEGAL_COMMAND;

		if (*(slash + COMMAND_10_BITS_SECOND_OPERAND_OFFSET) == '0')
			(*pCommand).operandCombinationType = (firstOperandType == LEFT_BITS)? LEFT_BITS_SRC_LEFT_BITS_DST : RIGHT_BITS_SRC_LEFT_BITS_DST;
		else
			if (*(slash + COMMAND_10_BITS_SECOND_OPERAND_OFFSET) == '1')
				(*pCommand).operandCombinationType = (firstOperandType == LEFT_BITS)? LEFT_BITS_SRC_RIGHT_BITS_DST : RIGHT_BITS_SRC_RIGHT_BITS_DST;
			else
				return ERROR_ILLEGAL_COMMAND;
		error = FillDbl(slash + COMMAND_10_BITS_DBL_OFFSET, pCommand);
	}

	if (error != ERROR_NONE)
		return error;

	int numberOfOperands = COMMANDS_OPERANDS_ARRAY[(*pCommand).commandType];

	
	error = FillOperands(commandStart, pCommand, numberOfOperands);
	if (error != ERROR_NONE)
		return error;
	
	
	return error;
}

/*********************************************************************************************************************** */
/* Description: Main method of this module - the method analyzes a string and fills the information to a line structure				 */
/* Input:			line - string to analyze line															*/
/* Input/Output:	pCommand - Command structure to fill with the command details								*/
/* Output:			Error type					*/
/*********************************************************************************************************************** */

void AnalyzeLine(char* line, LineStruct* pLineStruct)
{
	/* Initialize line struct with default values */
	pLineStruct->error = ERROR_NONE;
	pLineStruct->uLine.sCommand.operandCombinationType = (COMBINATION)0;
	
	strcpy(pLineStruct->uLine.sCommand.additionalWords[0], "");
	strcpy(pLineStruct->uLine.sCommand.additionalWords[1], "");
	strcpy(pLineStruct->uLine.sCommand.additionalWords[2], "");
	strcpy(pLineStruct->uLine.sCommand.additionalWords[3], "");

	pLineStruct->uLine.sCommand.additionalWordsCount = 0;
	pLineStruct->uLine.sCommand.addresses[0] = (ADDRESS_TYPE)0;
	pLineStruct->uLine.sCommand.addresses[1] = (ADDRESS_TYPE)0;
	pLineStruct->uLine.sCommand.addressIndexes[0] = (ADDRESS_INDEX_TYPE)0;
	pLineStruct->uLine.sCommand.addressIndexes[1] = (ADDRESS_INDEX_TYPE)0;
	pLineStruct->uLine.sCommand.registers[0] = 0;
	pLineStruct->uLine.sCommand.registers[1] = 0;
	
	strcpy(pLineStruct->label, "");

	/* Trim space and tab */
	trim(line);

	/* Ignore empty lines*/
	if (strlen(line) == 0)
		return;
	/* Ignore comment lines */
	if (line[0] == COMMENT_CHAR)
		return;
	
	ERROR_TYPE error = ERROR_NONE;

	if (strlen(line) > MAX_LINE_LENGTH)
	{
		pLineStruct->error = ERROR_LINE_LENGTH_EXCEEDS_80_CHARS;
		return;
	}

	/* label */
	char* label = SearchWithCpoy(line, END_OF_LABEL);
	int labelLength = 0;
	if (label != NULL)
	{
		error = CheckValidLabel(label);
		if (error == ERROR_NONE)
		{
			labelLength = strlen(label) + 1;
			memcpy(pLineStruct->label, label, labelLength - 1);
			pLineStruct->label[labelLength - 1] = '\0';
		}
	}
	free(label);

	if (error != ERROR_NONE)
	{
		pLineStruct->error = error;
		return;
	}

	/* operation */
	char* operationStart = strchr(line + labelLength, START_OF_OPERATION);
	if (operationStart != NULL)
	{
		/* Check that there are no characters between the label (if there is any) and the start of operation */
		if (IsNotEmpty(line + labelLength, (operationStart - line) - labelLength))
		{
			error = ERROR_ILLEGAL_OPERATION;
			pLineStruct->error = error;
			return;
		}

		error = FillOperationDetails(operationStart, &(pLineStruct->uLine.sOperation));
		pLineStruct->linetype = OPERATION;
		pLineStruct->error = error;
		return;
	}
	
	/* command */
	char* commandStart = line + labelLength;
	trim(commandStart);

	if (commandStart != NULL)
	{
		error = FillCommandDetails(commandStart, &(pLineStruct->uLine.sCommand));
		if (error == ERROR_NONE)
			pLineStruct->linetype = COMMAND;
	}
	
	if (error != ERROR_NONE)
	{
		pLineStruct->error = error;
		return;
	}
}

/*********************************************************************************************************************** */
/* Description: Transform a command to its numeric format by bits mnipulation
/* Input: commandStruct - line structure, from here I take all necessary information of the command 
/*********************************************************************************************************************** */
int produceMachineCode(Command* command)
{
	int comb=command->operandCombinationType;
	int destinationRegister=command->registers[1]<<2;
	int adressesDestination=command->addresses[1]<<5;
	int sourceRegister=command->registers[0]<<7;
	int adressesSource=command->addresses[0]<<10;
	int opCode=command->commandType<<12;
	int type=command->operandNumberBitsType<<16;
	int dbl=command->dbl<<17;

	return comb|destinationRegister|adressesDestination|sourceRegister|adressesSource|opCode|type|dbl;
}

